package ex1.ga;

import ex1.utils.*;


public class Chromosome {

	private boolean[] _representation;
	private int _fitness = Integer.MIN_VALUE;
	
	public Chromosome(int size)
	{
		_representation = new boolean[size];
		
		for (int i = 0; i < _representation.length; i++) {
			_representation[i] = false;
		}
	}
	
	public Chromosome(boolean[] bits)
	{
		_representation = bits;
	}
	
	public Chromosome(String val)
	{
		_representation = new boolean[val.length()];
		
		int i = 0;
		for (char charBit : val.toCharArray()) {
			_representation[i] = (charBit == '1') ? true : false;
			i++;
		}
	}

	public static Chromosome CreateRandomChromosome(int size)
	{
		Chromosome result = new Chromosome(size);
		for(int i = 0; i < size; i++)
		{
			result._representation[i] = Utils.RandomGenerator().nextBoolean();
		}
		
		return result;
	}
	
	public int updateFitness(FitnessFunction fitnessFunc)
	{
		if(_fitness == Integer.MIN_VALUE || fitnessFunc.fitnessCanChange())
		{
			this._fitness = fitnessFunc.getFitness(this);
		}
		return this._fitness;
	}
	
	/**
	 * Uniform Crossover
	 * @param otherParent
	 * @return
	 */
	public Pair<Chromosome, Chromosome> Reproduce(Chromosome otherParent)
	{		
		boolean[] repChild1 = new boolean[this._representation.length];
		boolean[] repChild2 = new boolean[this._representation.length];
		int i = 0;
		
		//copy the bits until the crossover point
		for( ; i < this._representation.length; i++)
		{
			boolean geneSelection = Utils.RandomGenerator().nextBoolean();
			if(geneSelection)
			{
				//child 1 gets this parent's values and child 2 gets the other parent's values
				repChild1[i] = this._representation[i];
				repChild2[i] = otherParent._representation[i];
			}
			else
			{
				//child 2 gets this parent's values and child 1 gets the other parent's values
				repChild1[i] = otherParent._representation[i];
				repChild2[i] = this._representation[i];				
			}
		}
		
		Chromosome child1 = new Chromosome(repChild1);
		Chromosome child2 = new Chromosome(repChild2);
		
		return new Pair<Chromosome, Chromosome>(child1, child2);
	}
	
	public void Mutate(double probabilityPerBit)
	{
		for (int i = 0; i < this._representation.length; i++)
		{
			if (Utils.RandomGenerator().nextDouble() < probabilityPerBit)
			{
				this._representation[i] = !this._representation[i];
			}
		}
	}

	public boolean[] getRepresentation()
	{
		return this._representation;
	}
	
	public int getFitness()
	{
		return this._fitness;
	}
	
	@Override
	public String toString() {
		
		StringBuilder sb = new StringBuilder();
		
		for (int i = 0; i < _representation.length; i++) 
		{
			
			sb.append(_representation[i] ? 1 : 0);
		}

		return sb.toString();
	}
}
